/*
 * app_main.c
 *
 *  Created on: 2022/06/20
 *      Author: mi
 */
#include <time.h>
#include "iid.h"
#include "app_main.h"
#include "miio_user_api.h"
#include "mible_log.h"
#include "mijia_profiles/stdio_service_server.h"
#include "mijia_profiles/lock_service_server.h"
#include "cryptography/mi_crypto_backend_msc.h"

#include "rtl876x_gpio.h"
#include "rtl876x_pinmux.h"
#include "mible_mcu.h"
#include "rtk_uart.h"
#include "rtk_common.h"
//#include "lock_operation.h"
//#include "lock_setting.h"
#include "json/jsmi.h"
#include "json/fmt.h"
#include "third_party/mbedtls/base64.h"
#include "third_party/mbedtls/md5.h"

#include "gwin_global_config.h"
#include "gwin_spec.h"
#include "gwin_main_task.h"


/* NOTE: advertise will close after 30min if unreg.
 */
#define MIBEACON_ADV_TIMEOUT            (30*60*1000)
#define MIBEACON_ADV_ON                 (0xFFFFFFFF)
#define MIBEACON_ADV_OFF                (0)
extern system_param_t system_param;

static void *m_poll_second_timer = NULL;

bool ota_state = 0;
bool gatt_state = 0;

/* YOUR_JOB: Select OOB methods in custom_mi_config.h(NUMPAD_INPUT or QR_CODE_OUT or both).
 * You will receive SCHD_EVT_OOB_REQUEST event and
 * Use mi_schd_oob_rsp() to response oob info.
 * NOTE: demo use uart input & static qr_code(should be random for each device)
 */
bool need_kbd_input = false;
uint8_t is_msc_exist = 1;
uint8_t qr_code[16] = {
//    0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
    0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x30,0x31,0x32,0x33,0x34,0x35,
};



/* YOUR_JOB: Must Select at least 1 support method (default DEV_INFO_HARDWARE_VERSION).
 * You can select DEV_INFO_NEW_SN for every device
 */
static const uint8_t support_devinfo[] = {
#if MI_MCU_CMD_ENABLE
	DEV_INFO_MCU_VERSION,
#endif
    DEV_INFO_HARDWARE_VERSION,
    //DEV_INFO_NEW_SN,
};

uint16_t mi_connect_handle = 0xFFFF;    /**< Handle of the current connection. */
extern void time_correction(time_t new_time);
extern void mible_record_clear(void);
extern void user_record_clear(void);

static void user_devinfo_callback(dev_info_type_t type, dev_info_t* buf)
{
    switch(type){
    case DEV_INFO_SUPPORT:
        buf->len = sizeof(support_devinfo);
        memcpy(buf->buff, support_devinfo, buf->len);
        break;
    case DEV_INFO_NEW_SN:
        buf->len = strlen("123456789/123456789/123456789/123456789/123456789/123456789/12");
        memcpy(buf->buff, "123456789/123456789/123456789/123456789/123456789/123456789/12", buf->len);
        break;
    case DEV_INFO_HARDWARE_VERSION:
        buf->len = strlen("RTL8762C");
        memcpy(buf->buff, "RTL8762C", buf->len);
        break;
    default:
        buf->code = MI_ERR_NOT_FOUND;
        return;
    }
    buf->code = MI_SUCCESS;
    return;
}

static void poll_second_timeout_handle(void *p_context)
{
    //xiaomi_lock.s_utc = time(NULL);//mible_systime_utc_get();
    MI_LOG_DEBUG("UTC: %u, memory free %d\n!", time(NULL), 
                os_mem_peek(RAM_TYPE_DATA_ON) + os_mem_peek(RAM_TYPE_BUFFER_ON));
}


void mi_schd_event_handler(schd_evt_t *p_event)
{
    MI_LOG_INFO("USER CUSTOM CALLBACK RECV EVT ID %d\n", p_event->id);
    switch (p_event->id) {
    case SCHD_EVT_OOB_REQUEST:
        MI_LOG_INFO("App selected IO cap is 0x%04X\n", p_event->data.IO_capability);
        switch (p_event->data.IO_capability) {
            // 配网期间，选择验证码方式
        case 0x0001:
            need_kbd_input = true;
            MI_LOG_INFO(MI_LOG_COLOR_GREEN "Please input your pair code ( MUST be 6 digits ) : \n");
            break;

            // 配网期间，二维码方式，扫描成功
        case 0x0080:
            mi_schd_oob_rsp(qr_code, 16);
            MI_LOG_INFO(MI_LOG_COLOR_GREEN "Please scan device QR code.\n");
            break;

        default:
            MI_LOG_ERROR("Selected IO cap is not supported.\n");
            mible_gap_disconnect(0);
        }
        break;

	case SCHD_EVT_KEY_DEL_FAIL:
	case SCHD_EVT_KEY_DEL_SUCC:
        miio_system_reboot();
        break;

        // 插件发起连接，连接失败
    case SCHD_EVT_ADMIN_LOGIN_FAILED:
        break;
        // 插件发起连接，连接成功
    case SCHD_EVT_ADMIN_LOGIN_SUCCESS:
        break;

        // 配网成功
    case SCHD_EVT_REG_SUCCESS:
        // device has been reg.
        MI_LOG_INFO("Reg success, UTC = %u\n", p_event->data.utc_time);
        //mible_systime_utc_set(p_event->data.utc_time);
        time_correction(p_event->data.utc_time);
        miio_ble_user_adv_init(0);
        gwin_main_ble_set_adv_interval(1, 0);
        miio_ble_set_adv_timeout(MIBEACON_ADV_ON);
        gwin_set_network_provisioned(1);
        break;

        // 配网后上电
    case SCHD_EVT_KEY_FOUND:{
        uint8_t did_byte[8] = {0};
        uint32_t did_num = 0;
        get_mi_device_id(did_byte);
        did_byte[0] = did_byte[7];did_byte[1] = did_byte[6];
        did_byte[2] = did_byte[5];did_byte[3] = did_byte[4];
        memcpy(&did_num, did_byte, sizeof(did_num));
        MI_LOG_INFO("Device ID: %u, Firmware ver:"MIBLE_LIB_AND_DEVELOPER_VERSION, did_num);
        miio_ble_user_adv_init(0);
        gwin_main_ble_set_adv_interval(1, 0);
        miio_ble_set_adv_timeout(MIBEACON_ADV_ON);
        gwin_set_network_provisioned(1);
    }break;

        // 待配网上电
    case SCHD_EVT_KEY_NOT_FOUND:{
        uint8_t did_byte[8] = {0};
        uint32_t did_num = 0;
        get_mi_device_id(did_byte);
        did_byte[0] = did_byte[7];did_byte[1] = did_byte[6];
        did_byte[2] = did_byte[5];did_byte[3] = did_byte[4];
        memcpy(&did_num, did_byte, sizeof(did_num));
        MI_LOG_INFO("Device ID: %u, Firmware ver:"MIBLE_LIB_AND_DEVELOPER_VERSION, did_num);
        miio_ble_user_adv_init(0);
        gwin_main_ble_set_adv_interval(1, 0);
        miio_ble_set_adv_timeout(system_param.product.network_timeout);
    }break;

        // 待配网上电
    case SCHD_EVT_KEYINFO_NOT_FOUND:
        break;

    case SCHD_EVT_MSC_SELF_TEST_PASS:
        //SYSTEM_DEBUG("SCHD_EVT_MSC_SELF_TEST_PASS");
        gwin_main_ble_set_cfg_info_pkt();
        break;
    

    case SCHD_EVT_MSC_SELF_TEST_FAIL:
        //SYSTEM_DEBUG("SCHD_EVT_MSC_SELF_TEST_FAIL");
        break;

    default:
        break;
    }
}

static void gap_event_handler(mible_gap_evt_t evt, mible_gap_evt_param_t* param)
{
    switch (evt) {
    case MIBLE_GAP_EVT_CONNECTED:
        mi_connect_handle = param->conn_handle;
        gatt_state = true;
        gwin_set_ble_connect(1);
        break;
    case MIBLE_GAP_EVT_DISCONNECT:
        mi_connect_handle = 0xFFFF;
        gatt_state = false;
        gwin_set_ble_connect(0);
        break;
    default:
        break;
    }
}

static void arch_event_handler(mible_arch_event_t evt, mible_arch_evt_param_t* param)
{
    switch (evt) {
    case MIBLE_ARCH_EVT_MIBEACON_SEND:{
        MI_LOG_INFO("mibeacon sended id %04x, len %d, remain %d, seq %d.\n", param->mibeacon.object_id,
                param->mibeacon.len, param->mibeacon.remain, param->mibeacon.seqnum);
        MI_LOG_HEXDUMP(param->mibeacon.val, param->mibeacon.len);
        if(0 == param->mibeacon.object_id){
            MI_LOG_INFO("mibeacon sent all ok!");
            break;
        }
    }break;

    default:
        break;
    }
}

static void app_dfu_callback(mible_dfu_state_t state, mible_dfu_param_t *param)
{
    switch (state)
    {
    case MIBLE_DFU_STATE_START:
        ota_state = 1;
        gwin_set_ota_state(ota_state);
        MI_LOG_INFO("fragment size is %d\n", param->start.fragment_size);
        break;
    case MIBLE_DFU_STATE_UPGRADE_STATUS:
        MI_LOG_INFO("upgrad status is %x\n", param->upgrade_status.req);
        param->upgrade_status.rsp = param->upgrade_status.req;
        break;
    case MIBLE_DFU_STATE_TRANSFER:
        MI_LOG_INFO("last fragment index is %d\n", param->trans.last_index);
        break;
    case MIBLE_DFU_STATE_VERIFY:
        MI_LOG_INFO("verify result is %d, version %u.%u.%u_%04u\n", param->verify.value,
            param->verify.version.major, param->verify.version.minor, 
            param->verify.version.revision, param->verify.version.build);
        //TODO: param->verify.value return MIBLE_DFU_STATUS_ERR_INVALID_VERSION when verion is old
        break;
    case MIBLE_DFU_STATE_SWITCH:
        MI_LOG_INFO("switch to new firmware\n");
        ota_state = 0;
        gwin_set_ota_state(ota_state);
        break;
    case MIBLE_DFU_STATE_CANCEL:
        ota_state = 0;
        gwin_set_ota_state(ota_state);
        MI_LOG_INFO("the sequence is canceled\n");
        break;
    default:
        MI_LOG_INFO("state of DFU is unknown %d\n", state);
        break;
    }
#if MI_MCU_CMD_ENABLE
    rtk_mcu_status(state, param);
#endif
}

int gpio_event_callback(uint8_t type, void *data)
{
    switch(type){
//        case USER_EVENT_SHORT_PRESS:
//            MI_LOG_INFO("Short press\n");
//            break;
//        case USER_EVENT_LONG_PRESS:
//            MI_LOG_INFO("Long press\n");
//            break;
//        case USER_EVENT_LONGLONG_PRESS:
//            MI_LOG_INFO("Longlong press\n");
//            break;
//        case USER_EVENT_RESET_PRESS:
//            MI_LOG_INFO("reboot...\n");
//            break;
        default:
            break;
    }
    return 0;
}

static const iic_config_t msc_iic_config = {
        .scl_pin = GWIN_PIN_IIC_SCL_MSC,
        .sda_pin = GWIN_PIN_IIC_SDA_MSC,
        .freq = IIC_400K,
};

static int msc_pwr_manage(bool power_stat)
{
    if (power_stat == 1) {
        Pad_Config(GWIN_PIN_PWR_MSC, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_ENABLE, PAD_OUT_HIGH);
    } else {
        Pad_Config(GWIN_PIN_PWR_MSC, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_ENABLE, PAD_OUT_LOW);
    }

	MI_LOG_INFO("msc_pwr_manage msc_power=%d\n", power_stat);
	
    return 0;
}

void user_app_init(void)
{
#if MI_MCU_CMD_ENABLE
    /* init uart TX_PIN RX_PIN WAKE_PIN in rtk_uart.h */
    mible_mcu_init();
	//miio_dfu_mcu_init();
#endif

    miio_timer_create(&m_poll_second_timer, poll_second_timeout_handle, MIBLE_TIMER_REPEATED);
    miio_timer_start(m_poll_second_timer, 10000, NULL);
    
    mible_libs_config_t config = {
        .msc_onoff        = msc_pwr_manage,
        .p_msc_iic_config = (void*)&msc_iic_config,
    };
    
    /* <!> mi_scheduler_init() must be called after ble_stack_init(). */
    mi_scheduler_init(10, mi_schd_event_handler, &config);

    mi_scheduler_start(SYS_MSC_SELF_TEST);

    mible_arch_register(arch_event_handler);
    mible_gap_register(gap_event_handler);
    miio_dfu_callback_register(app_dfu_callback);
    
    mi_service_init();
    miio_system_info_callback_register(user_devinfo_callback);

    miio_gatt_spec_init(gwin_spec_on_property_set, gwin_spec_on_property_get, gwin_spec_on_action_invoke, 1024, 16);
    
    miio_ble_user_adv_init(0);
//    miio_ble_user_adv_start(BLE_ADV_INTERVAL);
    gwin_custom_system_param_init_after_ble();
    
    //should execute at the end of init
    mi_scheduler_start(SYS_KEY_RESTORE);
    //pre check when reg auth succ
    //mi_reg_precheck_register(mi_reg_precheck_cb);
    
    os_delay(100);
}

void user_app_main_thread(void)
{
    mi_schd_process();
    return;
}
